/*
 *  icmloader.c
 *  iCodeMacRun
 *
 *  Created by Andrew Barry on 10/09/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include <sys/mman.h>

#include "icmloader.h"

struct LoadedProgram *icmload(const char *codePath, const char *linkPath)
{
	FILE *f = fopen(codePath, "rb");
	fseek(f, 0, SEEK_END);
	int codeLen = ftell(f);
	
	unsigned char *codeBuf = mmap(NULL, codeLen, PROT_EXEC | PROT_READ | PROT_WRITE,
						MAP_PRIVATE | MAP_ANON, -1, 0);

	fseek(f, 0, SEEK_SET);
	fread(codeBuf, codeLen, 1, f);
	fclose(f);
	
	f = fopen(linkPath, "rb");
	fseek(f, 0, SEEK_END);
	int linkLen = ftell(f);
	
	unsigned char *linkMap = malloc(linkLen);
	
	fseek(f, 0, SEEK_SET);
	fread(linkMap, linkLen, 1, f);
	fclose(f);
	
	// apply link map
	int pos = 0;
		
	while (linkMap[pos]) {
		int op = linkMap[pos++];
		//		printf("op: %d\n", op);
		if (op == 100) {
			pos += 4;
		} else if (op == 101) {
			pos += 4;
		} else if (op == 103) {
			pos += 4;
			int breakpointCount = *(int *) (linkMap + pos);
			pos += 4;
			int idx = 0;
			while (idx < breakpointCount) {
				int scriptLen = *(int *) (linkMap + pos);
				pos += 4;
				pos += scriptLen;
				int count = *(int *) (linkMap + pos);
				pos += 4;
				idx += count;
				pos += 4 * count;
			}
		} else if (op == 1) {
			// function defn
			pos += 8;
		} else if (op == 2) {
			int slen = *(int *) (linkMap + pos);
			pos += 4;
			
			char *fn = (char *) (linkMap + pos);
			pos += slen;
			int proc = 0;
			/*
			char *temp = malloc(slen + 1);
			memcpy(temp, fn, slen);
			temp[slen] = 0;
			printf("binding %s\n", temp);
			free(temp);
			*/
			
			if (slen == 4) {
				if (!strncmp(fn, "jslt", slen))
					proc = (int) jslt;
				else if (!strncmp(fn, "jseq", slen))
					proc = (int) jseq;
				else if (!strncmp(fn, "jsne", slen))
					proc = (int) jsne;
				else if (!strncmp(fn, "jsgt", slen))
					proc = (int) jsgt;
				else if (!strncmp(fn, "jsge", slen))
					proc = (int) jsge;
				else if (!strncmp(fn, "jsle", slen))
					proc = (int) jsle;
				else if (!strncmp(fn, "ifeq", slen))
					proc = (int) ifeq;
				else if (!strncmp(fn, "ifne", slen))
					proc = (int) ifne;
				else if (!strncmp(fn, "jsor", slen))
					proc = (int) jsor;
			} else if (slen == 5) {
				if (!strncmp(fn, "jsadd", slen))
					proc = (int) jsadd;
				else if (!strncmp(fn, "jssub", slen))
					proc = (int) jssub;
				else if (!strncmp(fn, "jsdiv", slen))
					proc = (int) jsdiv;
				else if (!strncmp(fn, "jsmul", slen))
					proc = (int) jsmul;
				else if (!strncmp(fn, "jsinc", slen))
					proc = (int) jsinc;
				else if (!strncmp(fn, "jsdec", slen))
					proc = (int) jsdec;
				else if (!strncmp(fn, "jsand", slen))
					proc = (int) jsand;
				else if (!strncmp(fn, "jsnot", slen))
					proc = (int) jsnot;
				else if (!strncmp(fn, "jsneg", slen))
					proc = (int) jsneg;
				else if (!strncmp(fn, "jslsh", slen))
					proc = (int) jslsh;
				else if (!strncmp(fn, "jsrsh", slen))
					proc = (int) jsrsh;
				else if (!strncmp(fn, "jsnew", slen))
					proc = (int) jsnew;
				else if (!strncmp(fn, "trace", slen))
					proc = (int) trace;
			} else if (slen == 6) {
				if (!strncmp(fn, "jseqeq", slen))
					proc = (int) jseqeq;
				else if (!strncmp(fn, "jsnene", slen))
					proc = (int) jsnene;
				else if (!strncmp(fn, "jspush", slen))
					proc = (int) jspush;
				else if (!strncmp(fn, "jssett", slen))
					proc = (int) jssett;
				else if (!strncmp(fn, "jsgett", slen))
					proc = (int) jsgett;
			} else if (slen == 8) {
				if (!strncmp(fn, "jsinvoke", slen))
					proc = (int) jsinvoke;
			} else if (slen == 9) {
				if (!strncmp(fn, "jsclosure", slen))
					proc = (int) jsclosure;
				else if (!strncmp(fn, "jsgetelem", slen))
					proc = (int) jsgetelem;
				else if (!strncmp(fn, "jssetelem", slen))
					proc = (int) jssetelem;
			} else if (slen == 10) {
				if (!strncmp(fn, "leaveFrame", slen))
					proc = (int) leaveFrame;
				else if (!strncmp(fn, "enterStack", slen))
					proc = (int) enterStack;
				else if (!strncmp(fn, "copyParams", slen))
					proc = (int) copyParams;
				else if (!strncmp(fn, "jsnewarray", slen))
					proc = (int) jsnewarray;
				else if (!strncmp(fn, "jsgetattrt", slen))
					proc = (int) jsgetattrt;
				else if (!strncmp(fn, "jsgetdesct", slen))
					proc = (int) jsgetdesct;
			} else if (slen == 11) {
				if (!strncmp(fn, "jsnewobject", slen))
					proc = (int) jsnewobject;
			} else if (slen == 15) {
				if (!strncmp(fn, "enterStackDebug", slen))
					proc = (int) enterStackDebug;
			} else if (slen == 22) {
				if (!strncmp(fn, "jsDebugCheckBreakpoint", slen))
					proc = (int) jsDebugCheckBreakpoint;
			}
			
			if (proc == 0)
			{
				printf("missing link entry\n");
				exit(1);
			}
			
			int off;
			while (off = *(int *) (linkMap + pos)) {
				int delta = proc - (int) (codeBuf + off);
				*(int *) (codeBuf + off - 4) = delta;
				pos += 4;
			}
			
			pos += 4;
		} else if (op == 3) {
			pos += 12;
		} else if (op == 5) {
			int slen = *(int *) (linkMap + pos);
			pos += 4;
			for (int idx = 0; idx < slen; idx++) {
				int off = *(int *) (linkMap + pos);
				pos += 4;
				*(int *) (codeBuf + off - 4) += (int) codeBuf;
			}
		} else
			break;
	}
	
	
	mprotect(codeBuf, codeLen, PROT_EXEC | PROT_READ);
	
	struct LoadedProgram *prog = malloc(sizeof(struct LoadedProgram));
	prog->codeBuf = codeBuf;
	prog->linkMap = linkMap;
	
	return prog;
}

jscontext *executeProgram(struct LoadedProgram *prog)
{
	unsigned char *linkMap = prog->linkMap;
	unsigned char *code = prog->codeBuf;
	int pos = 0;
	jscontext *ctx;
	
	while (linkMap[pos]) {
		int op = linkMap[pos++];
		//		printf("op: %d\n", op);
		if (op == 100) {
			int slotCount = *(int *) (linkMap + pos);
			pos += 4;
			ctx = jsnewcontext(slotCount);
			initRuntime(ctx);
		} else if (op == 101) {
			int offset = *(int *) (linkMap + pos);
			pos += 4;
			void (*proc)(jscontext *, void *, void *, int) = (code + offset);
			proc(ctx, nil, nil, 0);
		} else if (op == 102) {
			int offset = *(int *) (linkMap + pos);
			pos += 4;
			int len = *(int *) (linkMap + pos);
			pos += 4;
			unsigned char *strData = (unsigned char *) (linkMap + pos);
			pos += len;
			NSString *name = [[NSString alloc] initWithBytes:strData length:len encoding:NSUTF8StringEncoding];
			NSString *path = [[NSBundle mainBundle] pathForResource:[name stringByDeletingPathExtension] ofType:[name pathExtension]];
			NSImage *img = [[NSImage alloc] initByReferencingFile:path];
			[img setFlipped:YES];
			
			ctx->slots[offset] = bindImage(ctx, img);
		} else if (op == 103) {
			int slotCount = *(int *) (linkMap + pos);
			pos += 4;
			int breakpointCount = *(int *) (linkMap + pos);
			pos += 4;
			
			short *breakpoints = malloc(sizeof(short) * breakpointCount);
			char **breakpointScripts = malloc(sizeof(char *) * breakpointCount);
			int idx = 0;
			while (idx < breakpointCount) {
				int scriptLen = *(int *) (linkMap + pos);
				pos += 4;
				char *script = malloc(scriptLen + 1);
				memcpy(script, linkMap + pos, scriptLen);
				script[scriptLen] = 0;
				pos += scriptLen;
				int count = *(int *) (linkMap + pos);
				pos += 4;
				
				printf("script: %s\n", script);

				while (count) {
					breakpoints[idx] = *(int *) (linkMap + pos);
					pos+=4;
					breakpointScripts[idx] = script;
					idx++;
					count--;
				}
			}
			
			
			ctx = jsnewdebugcontext(slotCount, breakpointCount, breakpoints, breakpointScripts);
			initRuntime(ctx);
		} else if (op == 1) {
			int slot = *(int *) (linkMap + pos);
			pos += 4;
			int off = *(int *) (linkMap + pos);
			pos += 4;
			ctx->slots[slot]  = bindFunction(ctx, (void *) (code + off)); 
			//			printf("fn into slot %d\n", slot);
			
		} else if (op == 2) {
			int slen = *(int *) (linkMap + pos);
			pos += 4;
			
			pos += slen;
			
			int off;
			while (off = *(int *) (linkMap + pos)) {
				pos += 4;
			}
			
			pos += 4;
		} else if (op == 3) {
			int off = *(int *) (linkMap + pos);
			pos += 4;
			double dv = *(double *) (linkMap + pos);
			pos += 8;
			ctx->slots[off] = jsnum(ctx, dv);
			
		} else if (op == 4) {
			int off = *(int *) (linkMap + pos);
			pos += 4;
			int len = *(int *) (linkMap + pos);
			pos += 4;
			unsigned short *strData = (unsigned short *) (linkMap + pos);
			pos += len * 2;
			ctx->slots[off] = jsstrnconst(ctx, strData, len);
		} else if (op == 5) {
			int slen = *(int *) (linkMap + pos);
			pos += 4;
			pos += 4 * slen;
		} else {
			printf("unknown op %d\n", op);
			exit(1);
			break;
		}
	}
	return ctx;
}
